#include "example_framework.hh"
#include "../../protocol_files/common/generated_proto_header.hh"

#include "dummy.h"
#include "../../plugin/base/entity.hh"
#include "../../plugin/base/component_factory.hh"
#include "../../plugin/base/rpc_base.hh"
#include "../../interface_pool/mycomponent.hh"
#include "../../protocol_files/rpc/rpc_generated.hh"
#include "rpc_sample.hh"

static constexpr std::size_t DUMMY_COUNT = 1;
static EntityID idStart = reinterpret_cast<EntityID>(&FrameworkRef);
static int32_t RPCCount = 0;
static int32_t EntityRPCCount = 0;

class MyTracker : public engine::ComponentTracker {
public:
    virtual void onVanish(const std::string& name) override {
        verblog << "onVanish, Component unregistered, name:" << name << endlog;
        FrameworkRef.vanishComponent();
    }
};

static MyTracker myTracker;

bool ExampleFramework::onInstall(kconfig::Config* config) {
    RPCSkeleton::install();
    // NOTICE Just for testing and donot destroy
    RPCSkeleton::registerImpl(new RPCSample());
    return true;
}

bool ExampleFramework::onUninstall() {
    RPCSkeleton::uninstall();
    return true;
}

bool ExampleFramework::onStart(kconfig::Config * config)  {
    verblog << "Module example started." << endlog;
    // Search other entity every 1 second
    searchEntity(2, nullptr, SearchType::NONE, 1000);
    startTimer(1000, [&](TimerHandle, UserData) {
        verblog << "RPCCount:" << RPCCount << ", EntityRPCCount:" << EntityRPCCount << endlog;
    });
    startTimerOnce(5000, [&](TimerHandle, UserData) {
        // Create 100 AddressEntity
        auto myId = idStart;
        for (auto i = 0; i < DUMMY_COUNT; i++) {
            auto object = createEntity<Dummy>();
            object->resolve(myId++);
            dummyVector_.push_back(object);
        }
        startTimer(100, [&](TimerHandle, UserData) {
            if (dummyVector_.empty()) {
                auto myId = idStart;
                for (auto i = 0; i < DUMMY_COUNT; i++) {
                    auto object = createEntity<Dummy>();
                    object->resolve(myId++);
                    dummyVector_.push_back(object);
                }
                return;
            }
            RPCTest();
            searchEntity(2, [&](std::uint32_t, const EntityIDVector& result, std::uint64_t) {
                DummyReq req;
                // talk to all entity
                for (auto id : result) {
                    auto proxy = NewEntityProxy(id, Sample);
                    proxy->echo(req,
                        [&](const DummyAck&) {
                            EntityRPCCount++;
                        }
                    );
                }
            }, SearchType::NONE, 1000);
        });
        //startTimer(5000, [&](TimerHandle, UserData) {
        //    for (auto& dummy : dummyVector_) {
        //        dummy->dispose();
        //    }
        //    dummyVector_.clear();
        //    verblog << "Engine memory pool size in bytes:" << ModuleRef.getEngineMemoryPoolSize() << endlog;
        //});
    });
    return true;
}

bool ExampleFramework::onStop() {
    verblog << "Module example stopped." << endlog;
    return true;
}

void ExampleFramework::vanishComponent() {
    componentRef_ = nullptr;
}

void ExampleFramework::RPCTest() {
    if (pathID_) {
        DummyReq req;
        req.set_greetings("hello RPC");
        auto pathProxy = NewPathProxy(pathID_, Sample);
        pathProxy->echo(
            req,
        [&](const DummyAck& ack) {
            RPCCount++;
        },
            []() {
            verblog << "Path RPC timeout" << endlog;
        }
        );
    }
}

void ExampleFramework::onSiteJoin(const kratos::grid::SiteID & siteID) {
    if (siteID.type() == 3) {
        findPath(3, 0);
    }
}

void ExampleFramework::onPathJoin(PathID pathID, SiteType siteType, UserData userData) {
    verblog << "Path joined" << endlog;
    pathID_ = pathID;
}

